/**
 * 
 */
package craneding.gwt.databean.client.ui;

import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import com.google.gwt.i18n.client.DateTimeFormat;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.Element;
import com.google.gwt.user.client.Event;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.FlexTable;
import com.google.gwt.user.client.ui.HTML;

import craneding.gwt.databean.client.model.DynamicBean;
import craneding.gwt.databean.client.model.EnumType;
import craneding.gwt.databean.client.model.FieldInfo;
import craneding.gwt.databean.client.model.ReflectibleBean;

/**
 * Simple Bean Grid
 * 
 * @author crane.ding
 * @version 1.0 2008-12-6
 */
public class SimpleBeanGrid extends Composite {

	public interface SimpleBeanGridTableListener {
		void onCellClicked(ReflectibleBean pojo);
	}

	/**
	 * have a default css and Click Listener.
	 * 
	 * @author crane.ding
	 * @version 1.0 2008-12-7
	 */
	private class MyFlexTable extends FlexTable {
		private int selectline = 0;
		private boolean isOnMouseUp = false;
		private int left;
		private int index;
		private int width;
		private int width2;

		public MyFlexTable() {
			sinkEvents(Event.ONMOUSEOVER);
			sinkEvents(Event.ONMOUSEOUT);
			sinkEvents(Event.ONMOUSEUP);
			sinkEvents(Event.ONCLICK);
			sinkEvents(Event.ONMOUSEDOWN);
			sinkEvents(Event.ONMOUSEMOVE);
			setWidth("100%");
			setCellPadding(0);
			setCellSpacing(0);
			setStyleName("DataBean-SimpleBeanGrid");
		}

		@Override
		public void onBrowserEvent(Event event) {
			super.onBrowserEvent(event);
			int type = DOM.eventGetType(event);
			Element td = getEventTargetCell(event);
			if (td == null) {
				return;
			}
			Element tr = DOM.getParent(td);
			Element body = DOM.getParent(tr);
			int row = DOM.getChildIndex(body, tr);
			int column = DOM.getChildIndex(tr, td);
			int x = DOM.eventGetClientX(event)
					- DOM.getAbsoluteLeft(getElement())
					+ DOM.getElementPropertyInt(getElement(), "scrollLeft")
					+ Window.getScrollLeft();
			switch (type) {
			case Event.ONMOUSEOVER:
				if (row != 0)
					onMouseOver(row);
				break;
			case Event.ONMOUSEOUT:
				if (row != 0)
					onMouseOut(row);
				break;
			case Event.ONCLICK:
				if (row != 0)
					onClick(row);
				break;
			case Event.ONMOUSEUP:
				isOnMouseUp = false;
				DOM.setStyleAttribute(getElement(), "cursor", "default");
				break;
			case Event.ONMOUSEDOWN:
				if (row == 0 && column % 2 != 0) {
					left = x;
					index = column - 1;
					onMouseDown();
				}
				break;
			case Event.ONMOUSEMOVE:
				onMouseMove(x);
				break;
			default:
				break;
			}
		}

		private void onMouseDown() {
			isOnMouseUp = true;
			Element e = getCellFormatter().getElement(0, index);
			width = e.getOffsetWidth();
			Element e2 = getCellFormatter().getElement(0, index + 2);
			width2 = e2.getOffsetWidth();
			DOM.setStyleAttribute(getElement(), "cursor", "col-resize");
		}

		private void onMouseMove(int left) {
			if (isOnMouseUp) {
				int width = this.width + (left - this.left);
				int width2 = this.width2 - (left - this.left);
				if (width < 20 || width2 < 20)
					return;
				getColumnFormatter().setWidth(index, width + "px");
				getColumnFormatter().setWidth(index + 2, width2 + "px");
			}
		}

		private void onClick(int row) {
			if (selectline != 0)
				getRowFormatter().setStyleName(selectline,
						selectline % 2 == 0 ? "dualline" : "uniline");
			selectline = row;
			getRowFormatter().setStyleName(row, "selectline");
			ReflectibleBean bean = dataSource.get(row - 1);
			for (SimpleBeanGridTableListener listener : listeners) {
				dynamicBean.setDelegator(bean);
				listener.onCellClicked(bean);
			}

		}

		private void onMouseOut(int row) {
			String styleName = "uniline";
			if (row == selectline)
				styleName = "selectline";
			else if (row % 2 == 0)
				styleName = "dualline";
			getRowFormatter().setStyleName(row, styleName);
		}

		private void onMouseOver(int row) {
			if (row != selectline)
				getRowFormatter().setStyleName(row, "overline");
		}
	}

	private final MyFlexTable table = new MyFlexTable();
	private String[] fields;
	private DynamicBean dynamicBean;
	private Map<String, Class<?>> childBeanTypes = new HashMap<String, Class<?>>();
	private List<? extends ReflectibleBean> dataSource;
	private List<SimpleBeanGridTableListener> listeners;

	public SimpleBeanGrid() {
		initWidget(table);
	}

	/**
	 * set field display order
	 * 
	 * @param fields
	 *            field's order
	 * @return {@link SimpleBeanGrid}
	 */
	public SimpleBeanGrid setFieldOrder(String... fields) {
		this.fields = fields;
		return this;
	}

	/**
	 * set bean's class type.<br>
	 * e.g. DynamicBean bean = GWT.create(some.class);
	 * 
	 * @param dynamicBean
	 *            a class which is implement ReflectibleBean.
	 * @return {@link SimpleBeanGrid}
	 */
	public SimpleBeanGrid setDynamicBean(DynamicBean dynamicBean) {
		this.dynamicBean = dynamicBean;
		return this;
	}

	/**
	 * set field's class type which is a bean's field.<br>
	 * e.g. DynamicBean bean = GWT.create(some.class);
	 * 
	 * @param field
	 *            bean's filed
	 * @param beanType
	 *            field's class type
	 * @return {@link SimpleBeanGrid}
	 */
	public SimpleBeanGrid setChildBeanTypes(String field, Class<?> beanType) {
		childBeanTypes.put(field, beanType);
		return this;
	}

	/**
	 * add simple bean grid's tableListener.
	 * 
	 * @param listener
	 *            tableListener
	 * @return {@link SimpleBeanGrid}
	 */
	public SimpleBeanGrid addSimpleBeanGridTableListener(
			SimpleBeanGridTableListener listener) {
		if (listeners == null)
			listeners = new ArrayList<SimpleBeanGridTableListener>();
		listeners.add(listener);
		return this;
	}

	public void setDataSource(List<? extends ReflectibleBean> dataSource) {
		this.dataSource = dataSource;
		veiwWidgetByDataSource();
	}

	private void veiwWidgetByDataSource() {
		// remove table's all row.
		int rowCount = table.getRowCount();
		for (int i = 0; i < rowCount; i++) {
			table.removeRow(0);
		}
		// set all field's label.
		int row = 0, column = 0;
		if (fields == null)
			fields = dynamicBean.getFieldNames();
		for (String field : fields) {
			if (column != 0) {
				table.setText(row, column++, "");
				table.getFlexCellFormatter().setStyleName(row, column - 1,
						"dividerlines-title");
			}
			FieldInfo fieldInfo = dynamicBean.getFieldInfo(field);
			table.setWidget(row, column++, new HTML(fieldInfo.label()));
		}
		table.getRowFormatter().setStyleName(row, "title");
		for (ReflectibleBean pojo : dataSource) {
			dynamicBean.setDelegator(pojo);
			row++;
			column = 0;
			for (String field : fields) {
				if (column != 0) {
					table.setText(row, column++, "");
					table.getFlexCellFormatter().setStyleName(row, column - 1,
							"dividerlines");
				}
				table.setWidget(row, column++, new HTML(getTextByDynamicBean(
						field, dynamicBean)));
			}
			String styleName = "uniline";
			if (row % 2 == 0)
				styleName = "dualline";
			table.getRowFormatter().setStyleName(row, styleName);

		}
	}

	private String getTextByDynamicBean(String field, DynamicBean dynamicBean) {
		Object text = dynamicBean.getField(field);
		if (text != null) {
			if (text instanceof Date)
				return DateTimeFormat.getFormat("yyyy-MM-dd HH:mm:ss").format(
						(Date) text);
			if (text instanceof EnumType<?>) {
				return ((EnumType<?>) text).label();
			}
			return text.toString();
		}
		return "";
	}
}
